
package com.hlkj.pay.app.merchant.ext.impl.hnapay;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import cn.hutool.core.date.DatePattern;
import cn.hutool.json.JSONObject;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantPaymentExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.payment.ChannelPaymentAccountContext;
import com.hlkj.pay.app.merchant.ext.dto.payment.ChannelPaymentCallBackContext;
import com.hlkj.pay.app.merchant.ext.dto.payment.ChannelPaymentCallBackDto;
import com.hlkj.pay.app.merchant.ext.dto.payment.ChannelPaymentContext;
import com.hlkj.pay.app.merchant.ext.impl.hnapay.constant.HnapayConstant;
import com.hlkj.pay.app.merchant.ext.impl.hnapay.dto.HnapayAppChannelParams;
import com.hlkj.pay.app.merchant.ext.impl.hnapay.handler.HnapayMerchantCommonParamsHandle;
import com.hlkj.pay.app.merchant.ext.impl.hnapay.utils.HttpUtils;
import com.hlkj.pay.app.merchant.ext.impl.hnapay.utils.RecepitUtil;
import com.hlkj.pay.dto.merchant.MerchantAcctBalanceDto;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.enums.MerchantEnum;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAppChannelDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.infrastructure.model.payment.MerchantPaymentDO;
import com.hlkj.pay.infrastructure.model.payment.MerchantPaymentDetailDO;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.HttpRequestUtil;
import com.hlkj.pay.util.MerchantUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author HlpayTeam
 * @date 2024/12/16 11:40
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_HNA_PAY)
public class HnapayMerchantPaymentExtPt implements ChannelMerchantPaymentExtPt {

    private final HnapayMerchantCommonParamsHandle hnapayMerchantCommonParamsHandle;

    public HnapayAppChannelParams aliPayAppChannelParams(MerchantAppChannelDO merchantAppChannelDO) {
        return JsonUtils.parseObject(merchantAppChannelDO.getAppConfigJson(), HnapayAppChannelParams.class);
    }

    @Override
    public void submitChannelSinglePayment(ChannelPaymentContext channelPayOrderContext) {
        try {
            MerchantPaymentDetailDO merchantPaymentDetailDO = channelPayOrderContext.getMerchantPaymentDetailDO();
            MerchantPaymentDO merchantPaymentDO = channelPayOrderContext.getMerchantPaymentDO();
            HnapayAppChannelParams aliPayAppChannelParams = aliPayAppChannelParams(channelPayOrderContext.getMerchantAppChannelDO());
            // 构造请求参数以调用接口
            String url = HnapayConstant.DOMAIN_PROD + HnapayConstant.MERCHANT_PAY;
            Map<String, String> params = new LinkedHashMap<>();
            params.put("version", "2.0"); // 版本
            params.put("tranCode", "SGP01"); // 交易代码
            params.put("merId", aliPayAppChannelParams.getMerId()); // 商户ID
            params.put("merOrderId", merchantPaymentDetailDO.getDetailPaymentId()); // 商户订单号
            params.put("submitTime", DateUtils.getDateString(DatePattern.PURE_DATETIME_PATTERN)); // 提交时间

            Map<String, String> msgMap = new LinkedHashMap<>();
            msgMap.put("tranAmt", MerchantUtils.bigDecimalToString2(merchantPaymentDetailDO.getPayAmount())); // 支付金额
            msgMap.put("payType", "1"); // 付款类型
            msgMap.put("auditFlag", "0"); // 是否需要复核
            msgMap.put("payeeName", merchantPaymentDetailDO.getName()); // 收款方姓名
            msgMap.put("payeeAccount", merchantPaymentDetailDO.getCardNo()); // 收款方账户 银行卡号或者新生登录名
            if (merchantPaymentDO.getEntryType() == 3) {
                msgMap.put("payeeAccType", "1"); // 收款方账号类型
            } else if (merchantPaymentDO.getEntryType() == 4) {
                msgMap.put("payeeAccType", "2"); // 收款方账号类型
            } else {
                throw new Exception("只支持银行卡和内部转账");
            }
            msgMap.put("note", merchantPaymentDO.getRemark()); // 附言
            msgMap.put("remark", merchantPaymentDO.getRemark()); // 付款说明
            msgMap.put("bankCode", (!StringUtils.hasText(merchantPaymentDetailDO.getBankCode())) ? "" : merchantPaymentDetailDO.getBankCode()); // 银行简码
            if (merchantPaymentDO.getPayeeType() == 1) {
                msgMap.put("payeeType", "1"); // 收款方类型
            } else {
                msgMap.put("payeeType", "2"); // 收款方类型
            }
            msgMap.put("notifyUrl", channelPayOrderContext.getNotifyUrl()); // 商户异步通知地址
            msgMap.put("paymentTerminalInfo", "01|"); //  付款方终端信息说明
            msgMap.put("deviceInfo", (!StringUtils.hasText(merchantPaymentDO.getClientIp())) ? "47.98.61.184" : merchantPaymentDO.getClientIp() + "||||||"); // 设备信息说明

            params.put("msgCiphertext", RecepitUtil.encrpt(msgMap, aliPayAppChannelParams.getHnaM2PublicKey()));
            params.put("signType", "3"); // 版本
            params.put("signValue", RecepitUtil.sign(aliPayAppChannelParams.getMchM2PrivateKey(), params)); // 版本
            params.put("charset", "1"); // 版本

            log.error("新生单笔转账接口 请求参数:{}，金额明文{}", JsonUtils.toJsonStringNotNull(params), JsonUtils.toJsonStringNotNull(msgMap));
            String responseStr = HttpUtils.postForm(url, params);
            log.error("新生单笔转账接口 返回参数:{}", responseStr);
            JSONObject response = JsonUtils.parseObj(responseStr);
            MerchantPaymentDetailDO updateTransferOrderDO = new MerchantPaymentDetailDO();
            if ("0000".equals(response.getStr("resultCode"))) {
                updateTransferOrderDO.setInsOrderNo(response.getStr("hnapayOrderId"));
                updateTransferOrderDO.setStatus(MerchantEnum.PROCESS_STATUS.DOING.getCode());
                channelPayOrderContext.setUpdateMerchantPaymentDetailDO(updateTransferOrderDO);
            }
            else {
                updateTransferOrderDO.setStatus(MerchantEnum.PROCESS_STATUS.FAIL.getCode());
                channelPayOrderContext.setProcessStatus(false);
                channelPayOrderContext.setRemark(response.getStr("errorMsg"));
            }
        }
        catch (Exception e) {
            channelPayOrderContext.setProcessStatus(false);
            channelPayOrderContext.setRemark(e.getMessage());
        }
    }

    @Override
    public void queryChannelPayment(ChannelPaymentContext channelPayOrderContext) {
        try {
            MerchantPaymentDetailDO merchantPaymentDetailDO = channelPayOrderContext.getMerchantPaymentDetailDO();
            HnapayAppChannelParams aliPayAppChannelParams = aliPayAppChannelParams(channelPayOrderContext.getMerchantAppChannelDO());

            Map<String, String> params = new LinkedHashMap<>();
            params.put("version", "2.0"); // 版本
            params.put("tranCode", "SGP02"); // 交易代码
            params.put("merId", aliPayAppChannelParams.getMerId()); // 商户ID
            params.put("merOrderId", merchantPaymentDetailDO.getDetailPaymentId()); // 商户订单号
            params.put("submitTime", DateUtils.dateToString(merchantPaymentDetailDO.getUpdateTime(), DatePattern.PURE_DATE_PATTERN)); // 提交时间

            params.put("signValue", RecepitUtil.sign(aliPayAppChannelParams.getMchM2PrivateKey(), params)); // 版本
            params.put("charset", "1"); // 版本
            params.put("signType", "3"); // 版本

            String url = HnapayConstant.DOMAIN_PROD + HnapayConstant.MERCHANT_PAY_QUERY;
            log.error("新生代付查询接口 请求参数:{}", JsonUtils.toJsonStringNotNull(params));
            String responseStr = HttpUtils.postForm(url, params);
            log.error("新生代付查询接口 返回参数:{}", responseStr);
            JSONObject response = JsonUtils.parseObj(responseStr);
            MerchantPaymentDetailDO updateTransferOrderDO = new MerchantPaymentDetailDO();
            if ("0000".equals(response.getStr("resultCode"))) {
                updateTransferOrderDO.setDetailPaymentId(merchantPaymentDetailDO.getDetailPaymentId());
                updateTransferOrderDO.setInsOrderNo(response.getStr("hnapayOrderId"));
                try {
                    updateTransferOrderDO.setSuccessTime(DateUtils.shanghaiDate(response.getStr("successTime"), DatePattern.PURE_DATETIME_PATTERN));
                }
                catch (Exception e) {
                    log.error("新生代付 查询返回 时间格式化错误 dateTime:{},message:{}", response.getStr("successTime"), e.getMessage());
                }
                // SUCCESS：转账成功； WAIT_PAY：等待支付； CLOSED：订单超时关闭； REFUND：退票； DEALING：转账到银行卡处理中；
                // FAIL：转账失败
                switch (response.getStr("orderStatus")) {
                    case "1":
                        updateTransferOrderDO.setStatus(MerchantEnum.PROCESS_STATUS.SUCCESS.getCode());
                        break;
                    case "3":
                    case "2":
                        updateTransferOrderDO.setStatus(MerchantEnum.PROCESS_STATUS.FAIL.getCode());
                        break;
                    default:
                        updateTransferOrderDO.setStatus(MerchantEnum.PROCESS_STATUS.DOING.getCode());
                }
            }
            else {
                updateTransferOrderDO.setStatus(MerchantEnum.PROCESS_STATUS.FAIL.getCode());
                channelPayOrderContext.setProcessStatus(false);
                channelPayOrderContext.setRemark(response.getStr("orderFailedMsg"));
            }
            channelPayOrderContext.setUpdateMerchantPaymentDetailDO(updateTransferOrderDO);
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void queryChannelAccount(ChannelPaymentAccountContext channelPaymentAccountContext) {
        try {
            // 初始化SDK
            Map<String, String> bodyMap = new LinkedHashMap<>();
            HnapayAppChannelParams aliPayAppChannelParams = aliPayAppChannelParams(channelPaymentAccountContext.getMerchantAppChannelDO());
            if (!StringUtils.hasText(aliPayAppChannelParams.getMerId())) {
                throw new Exception("未配置");
            }
            bodyMap.put("version", "2.0");
            bodyMap.put("tranCode", "QB01");
            bodyMap.put("merId", aliPayAppChannelParams.getMerId());
            bodyMap.put("acctType", "");
            bodyMap.put("charset", "1");
            bodyMap.put("signType", "3");
            bodyMap.put("signValue", RecepitUtil.sign(aliPayAppChannelParams.getMchM2PrivateKey(), bodyMap));

            String requestUrl = HnapayConstant.DOMAIN_PROD + HnapayConstant.QUERY_BALANCE;
            log.info("新生账户余额查询req参数:{}", JsonUtils.toJsonString(bodyMap));
            String response = HttpUtils.postForm(requestUrl, bodyMap);
            log.info("新生账户余额查询返回参数:{}", response);
            JSONObject respObj = JsonUtils.parseObj(response);
            if ("0000".equals(respObj.getStr("resultCode"))) {
                MerchantAcctBalanceDto merchantAcctBalanceDto = new MerchantAcctBalanceDto();
                merchantAcctBalanceDto.setBalance(MerchantUtils.stringToBigDecimal(respObj.getStr("avaBalance")));
                channelPaymentAccountContext.setMerchantAcctBalances(new ArrayList<>() {
                    {
                        add(merchantAcctBalanceDto);
                    }
                });
            } else {
                channelPaymentAccountContext.setRemark(respObj.getStr("errorMsg"));
                log.error("新生账户余额查询失败 message: {}", respObj.getStr("errorMsg"));
            }
        } catch (Exception e) {
            log.error("新生账户余额查询请求失败 message:{}", e.getMessage(), e);
            channelPaymentAccountContext.setRemark("新生账户余额查询请求失败,失败信息" + e.getMessage());
            channelPaymentAccountContext.setProcessStatus(Boolean.FALSE);
        }
    }

    @Override
    public void queryChannelPaymentProof(ChannelPaymentContext channelPayOrderContext) {
        try {
            MerchantPaymentDetailDO merchantPaymentDetailDO = channelPayOrderContext.getMerchantPaymentDetailDO();
            // 初始化SDK
            HnapayAppChannelParams aliPayAppChannelParams = aliPayAppChannelParams(channelPayOrderContext.getMerchantAppChannelDO());

            Map<String, String> bodyMap = new LinkedHashMap<>();
            bodyMap.put("version", "2.0");
            bodyMap.put("tranCode", "SGP03");
            bodyMap.put("merId", aliPayAppChannelParams.getMerId());
            bodyMap.put("merOrderId", merchantPaymentDetailDO.getDetailPaymentId());
            bodyMap.put("submitTime", DateUtils.getDateString(DatePattern.PURE_DATETIME_PATTERN)); // 提交时间

            Map<String, String> msgMap = new LinkedHashMap<>();
            msgMap.put("orgMerOrderId", ""); // 原商户付款订单号
            msgMap.put("orgSubmitTime", ""); // 原订单提交时间
            msgMap.put("hnapayOrderId", merchantPaymentDetailDO.getInsOrderNo()); // 新生订单号
            bodyMap.put("msgCiphertext", RecepitUtil.encrpt(msgMap, aliPayAppChannelParams.getHnaM2PublicKey()));
            bodyMap.put("signValue", RecepitUtil.sign(aliPayAppChannelParams.getMchM2PrivateKey(), bodyMap));
            bodyMap.put("charset", "1");
            bodyMap.put("signType", "3");

            String requestUrl = HnapayConstant.DOMAIN_PROD + HnapayConstant.MERCHANT_PAY_CERTIFICATE;
            log.info("新生付款凭证查询req参数:{}", JsonUtils.toJsonString(bodyMap));
            String response = HttpUtils.postForm(requestUrl, bodyMap);
            log.info("新生付款凭证查询返回参数:{}", response);
            JSONObject respObj = JsonUtils.parseObj(response);
            if ("0000".equals(respObj.getStr("resultCode"))) {
                byte[] bb = Base64.getDecoder().decode(respObj.getStr("payCertificate"));
                channelPayOrderContext.setVoucherFileByte(bb);
                channelPayOrderContext.setFileType(CommonEnum.OssEnum.IMAGE);
            }
        }
        catch (Exception e) {
            channelPayOrderContext.setProcessStatus(false);
            channelPayOrderContext.setRemark(e.getMessage());
        }
    }

    @Override
    public ChannelPaymentCallBackDto parseCallBackText(ChannelPaymentCallBackContext channelPaymentCallBackContext, HttpServletRequest request) {
        ChannelPaymentCallBackDto channelPaymentCallBackDto = new ChannelPaymentCallBackDto();

        try {
            HnapayAppChannelParams aliPayAppChannelParams = aliPayAppChannelParams(channelPaymentCallBackContext.getMerchantAppChannelDO());
            Map<String, String> params = HttpRequestUtil.requestFormParams(request);
            String results = JsonUtils.toJsonString(params);
            log.info("新生代付结果结果通知 响应结果 results:{}", results);

            boolean bool = RecepitUtil.verify(params.get("tranCode"), params, aliPayAppChannelParams.getHnaM2PublicKey(), params.get("signValue"));
            if (!bool) {
                channelPaymentCallBackDto.setVerifySign(false);
                channelPaymentCallBackDto.setRemark("新生代付回调验签失败");
                return channelPaymentCallBackDto;
            }
            MerchantPaymentDetailDO updateTransferOrderDO = new MerchantPaymentDetailDO();
            channelPaymentCallBackDto.setMerchantPaymentDetailDO(updateTransferOrderDO);
            updateTransferOrderDO.setDetailPaymentId(params.get("merOrderId"));
            if (!"0000".equals(params.get("resultCode"))) {
                updateTransferOrderDO.setStatus(MerchantEnum.PROCESS_STATUS.FAIL.getCode());
                channelPaymentCallBackDto.setRemark(params.get("errorMsg") + ", 错误码" + params.get("resultCode"));
                channelPaymentCallBackDto.setProcessStatus(false);
                return channelPaymentCallBackDto;
            }
            if (params.containsKey("errorCode") && StringUtils.hasText(params.get("errorCode"))) {
                updateTransferOrderDO.setStatus(MerchantEnum.PROCESS_STATUS.FAIL.getCode());
                if (StringUtils.hasText(params.get("errorMsg")))
                    updateTransferOrderDO.setRemark(params.get("errorMsg"));
                else
                    updateTransferOrderDO.setRemark(params.get("errorCode"));
                return channelPaymentCallBackDto;
            }

            updateTransferOrderDO.setInsOrderNo(params.get("hnapayOrderId"));
            updateTransferOrderDO.setStatus(MerchantEnum.PROCESS_STATUS.SUCCESS.getCode());
            try {
                updateTransferOrderDO.setSuccessTime(DateUtils.shanghaiDate(params.get("successTime"), DatePattern.PURE_DATETIME_PATTERN));
            } catch (Exception e) {
                updateTransferOrderDO.setSuccessTime(DateUtils.getCurrentTime());
            }
        }catch (Exception e) {
            log.error("新生代付回调解析异常： {}", e.getMessage(), e);
            channelPaymentCallBackDto.setRemark("新生代付回调解析异常：" + e.getMessage());
            channelPaymentCallBackDto.setProcessStatus(false);
        }
        return channelPaymentCallBackDto;
    }

    @Override
    public ChannelPaymentCallBackDto parseCallBackTextFix(HttpServletRequest servletRequest) {
        return null;
    }

    @Override
    public String processCallBack(MerchantAppChannelDO merchantAppChannelDO, CommonResult commonResult) {
        if (commonResult.isSuccess()) {
            return "RespCode=200";
        }
        return "Fail";
    }

    @Override
    public Boolean verify(ProviderChannelConfigDO providerChannelConfigDO, MerchantAppChannelDO merchantAppChannelDO, String accessCallBody) {
        HnapayAppChannelParams aliPayAppChannelParams = aliPayAppChannelParams(merchantAppChannelDO);
        try {
            // 编码格式
            Map<String, String> params = JsonUtils.parseObject(accessCallBody, Map.class);
            // 验签方法
            return RecepitUtil.verify(params.get("tranCode"), params, aliPayAppChannelParams.getHnaM2PublicKey(), params.get("signValue"));
        }
        catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public void notifyChannel(ChannelPaymentContext channelPaymentContext) {
    }

    @Override
    public void submitChannelSingleRecharge(ChannelPaymentContext channelPaymentContext) {
    }

    @Override
    public void notifyProofGenerate(ChannelPaymentContext channelPaymentContext) {
    }
}